#include "ThreadCache.h"
#include "CentralCache.h"

void* ThreadCache::Allocate(size_t size)
{
	// 无论5字节还是6字节，都会给出8字节，所以都要向上进行对齐
	assert(size <= MAX_BYTES);
	size_t alignSize = SizeClass::RoundUp(size); // 计算对齐数
	size_t index = SizeClass::Index(size); // 计算在哪个桶

	// 如果桶不为空，则直接取桶中的内存即可（ThreadCache内取内存，不够再去找CentralCache要）
	if (!_freeLists[index].Empty())
	{
		return _freeLists[index].Pop();
	}
	else
	{
		// 找下一层中心层获取对象
		return FetchFromCentralCache(index, alignSize);
	}

	return nullptr;
}

void ThreadCache::Deallocate(void* ptr, size_t size)
{
	assert(ptr); 
	assert(size <= MAX_BYTES);

	// 算出是哪个桶之后就将对象头插回去freeList
	size_t index = SizeClass::Index(size);
	_freeLists[index].Push(ptr);
}

// 找central cache获取内存对象
void* ThreadCache::FetchFromCentralCache(size_t index, size_t size)
{
	// 慢开始反馈调节算法:
	// 小对象（Byte）可以多给一点，大对象就少给一点

	// 1、最开始不会一次向centarl cache一次批量要太多，要太多了可能用不完
	// 2、如果你不要这个size大小内存需求，那么batchNum就会不断增长，直到上限
	// 3、size越大，一次向centarl cache要的batchNum就越小
	// 4、size越小，一次向centarl cache要的batchNum就越大（这个越大是慢慢增长的，也就是慢启动）

	size_t batchNum = std::min(_freeLists[index].MaxSize(), SizeClass::NumMoveSize(size));
	// 要小的那一个

	if (_freeLists[index].MaxSize() == batchNum)
	{
		_freeLists[index].MaxSize() += 1;
	}

	void* start = nullptr;
	void* end = nullptr;

	// 但是span中不一定有那么多个，实际上有多少个给多少个（actual）
	size_t actualNum = CentralCache::GetInStance()->FetchRangeObj(start, end, batchNum, size);

	assert(actualNum > 1); // 至少保证得有一个

	// 如果只获取到了一个那就直接返回
	if (actualNum == 1)
	{
		assert(start == end); // 如果只获取到一个，那么start和end应该是相等的
		return start;
	}
	else
	{
		// 如果获取了多个，那么应该把头返回，剩下的挂入链表中
		_freeLists[index].PushRange(NextObj(start), end); // start的头4/8字节指向下一个内存对象。
		return start;
	}
}
